﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Free2AskModel;
using Free2AskWeb.Models;
using System.Data.Objects;
using System.Web.Security;
using DynamicQueryLib;
using LinqKit;

namespace Free2AskWeb.Controllers
{
    public class QuestionController : Controller
    {
        //
        // GET: /Question/
        private Free2AskDBEntities db;

        public QuestionController()
        {
            db = new Free2AskDBEntities();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="tags">Space seperated.</param>
        /// <param name="keyword"></param>
        /// <param name="sort"></param>
        /// <returns></returns>
        [HttpGet]
        public ActionResult Search(string tagarray = "", string keyword = "", string sort = "")
        {
            //tags = "file copy";

            List<string> tagArray = (from t in tagarray.Split(' ')
                                     select t.Trim()).ToList();
            
            var questions = KeywordSearch(Tagged(tagArray), keyword);

            switch (sort)
            {
                case "newest"://order by posted time
                    questions = questions.OrderByDescending(q => q.DatetimePosted);
                    break;
                case "frequent": //order by votes and answers
                    questions = questions.OrderByDescending(q => q.Answers.Count+q.Votes.Sum(v=>v.Value));
                    break;
                case "unanswered": 
                    questions = questions.Where(q=>q.Answers.Count==0).OrderByDescending(q => q.DatetimePosted);
                    break;
                case "active": //order by answer date
                    questions = questions
                        //.Where(q => q.Answers.Count >0)
                        .OrderByDescending(q => (from a in q.Answers select a).OrderByDescending(x=>x.DatetimePosted).FirstOrDefault().DatetimePosted);
                    break;
            }

            QuestionsModel questionsModel = new QuestionsModel(questions.ToList());
            if (tagArray.Count == 1 && tagArray[0] == "")//means no tag.
            {
                questionsModel.Tags = (from t in db.Tags
                                       select t).ToList();
            }
            else
            {
                questionsModel.Tags = (from t in db.Tags
                                       where tagArray.Where(a => a.Trim() == t.Name.Trim()).Count() > 0
                                       select t).ToList();
            }

            ViewBag.tagarray = tagarray; ViewBag.keyword = keyword; ViewBag.sort = sort;

            return View(questionsModel);
        }

        private IQueryable<Question> KeywordSearch(IQueryable<Question> questions,string keyword)
        {
            List<DynamicQueryLib.Filter> filterList = new List<DynamicQueryLib.Filter>();

            if (keyword != "")
            {
                DynamicQueryLib.Filter f = new DynamicQueryLib.Filter();
                f.Connector = FilterConnectorType.AND;
                f.PropertyName = "Title";
                f.PropertyType = typeof(string);
                f.Values.Add(keyword);
                f.Operator = RelationalOperators.Contains;
                filterList.Add(f);
            }

            if (keyword != "")
            {
                DynamicQueryLib.Filter f = new DynamicQueryLib.Filter();
                f.Connector = FilterConnectorType.OR;
                f.PropertyName = "PostContent";
                f.PropertyType = typeof(string);
                f.Values.Add(keyword);
                f.Operator = RelationalOperators.Contains;
                filterList.Add(f);
            }

            var predicate = LambdaExpressionHelper<Question>.CreatePredicate<Question>(filterList);

            //return db.Posts.OfType<Question>().AsExpandable().Where(predicate);
            return questions.AsExpandable().Where(predicate);
        }

        private IQueryable<Question> Tagged(List<string> tags)
        {
            if (tags.Count == 1&&tags[0]=="")//end case
            {
                var questions = from q in db.Posts.OfType<Question>()
                                select q;
                return questions;
            }
            else if (tags.Count == 1)//end case
            {
                
                string tag = tags[0];
                var questions = from q in db.Posts.OfType<Question>()
                                where q.Tags.Where(t => t.Name.Trim() == tag.Trim()).Count()>0
                                select q;
                return questions;
            }
            else //recursive case
            {
                return Tagged(tags.GetRange(0,1)).Intersect(Tagged(tags.GetRange(1,tags.Count-1)));
            }
        }
        //[Authorize]
        public ActionResult Details(int questionID)
        {
            Question question=db.Posts.OfType<Question>().Single(q => q.ID == questionID);

            QuestionModel questionModel = new QuestionModel();
            //question.PostContent =HttpUtility.HtmlDecode( question.PostContent);
            questionModel.Question = question;
            questionModel.Answers = question.Answers.ToList();

            ViewBag.AuthorID = new Guid(Membership.GetUser().ProviderUserKey.ToString());
            return View(questionModel);
        }

        [HttpGet]
        [Authorize]
        public ActionResult Ask()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Ask(PostModel model)
        {
            if (model.Title == null || model.Content == null)
            {
                throw new Exception("Empty Title or Content!");
            }

            Question question = new Question();
            question.Title = model.Title;
            question.PostContent =HttpUtility.HtmlDecode(model.Content);
            question.DatetimePosted = DateTime.Now;
            question.AuthorID = new Guid(System.Web.Security.Membership.GetUser().ProviderUserKey.ToString());
            if (model.Tags != null)
            {
                foreach (var tagName in model.Tags)
                {
                    Tag tag = db.Tags.SingleOrDefault(t => t.Name.Trim().ToLower() == tagName.Trim().ToLower().Trim());
                    if (tag != null)
                    {
                        question.Tags.Add(tag);
                    }
                    else
                    {
                        question.Tags.Add(new Tag { Name = tagName });
                    }
                }
            }
            
            db.Posts.AddObject(question);
            db.SaveChanges();

            //return RedirectToAction("Index","Home");
            return Json(question.ID);
        }

        [HttpGet]
        public ActionResult Modify(int postID)
        {
            //Get post object(root).
            Post post = db.Posts.Single(q => q.ID == postID); 
            //Get subclass property values. A post can either be a question or an answer.
            GetPost_Result postResult = db.GetPost(postID).FirstOrDefault();
            //Assemble the view model
            PostModel model = new PostModel();
            if (post.Author != null)
            {
                model.AuthorName = post.Author.LoweredUserName;
            }
            model.Content = postResult.PostContent;
            model.PostID = postID;
            model.Title = postResult.Title;
            model.Tags = post.Tags.Select(t=>t.Name).ToArray<string>();
            model.PostType = post.GetPostType();      
            
            return View(model);
        }

        [HttpPost]
        public ActionResult Modify(PostModel postModel)
        {
            Post post = db.Posts.Single(p => p.ID == postModel.PostID);
            PostType type = post.GetPostType();
            if (type == PostType.Question)
            {
                UpdatePost<Question>(postModel);
            }
            else if (type == PostType.Answer)
            {
                UpdatePost<Answer>(postModel);
            }

            //Hey, look out! if the post is an answer, show its corresponding question's details.
            if (type == PostType.Question)
            {
                //return RedirectToAction("Details", new { questionID = post.ID });
                return Json(post.ID);
            }
            else
            {
                //return RedirectToAction("Details", new { questionID = db.Posts.OfType<Answer>().Single(p=>p.ID== post.ID).Question.ID });
                return Json( db.Posts.OfType<Answer>().Single(p => p.ID == post.ID).Question.ID );
            }
        }

        private void UpdatePost<T>(PostModel postModel)  where T:Post,IPost 
        {
            T post = db.Posts.OfType<T>().Single(p => p.ID == postModel.PostID);
            post.PostContent =HttpUtility.HtmlDecode(postModel.Content);
            post.Title = postModel.Title;
            //db.Posts.Attach(post);
            db.ObjectStateManager.ChangeObjectState(post, System.Data.EntityState.Modified);
            db.SaveChanges();
        }

        [Authorize]
        [HttpPost]
        public ActionResult Delete(int postID)
        {
            var post=db.Posts.Single(p=>p.ID==postID);
            PostType postType = post.GetPostType();
            int questionID = -1;
            if (postType == PostType.Answer)
            {
                questionID = db.Posts.OfType<Answer>().Single(p => p.ID == post.ID).Question.ID;
            }
            if (Roles.GetRolesForUser().Contains("admin") || new Guid(Membership.GetUser().ProviderUserKey.ToString()) == post.AuthorID)
            {//Allow deleting
                //Clear Tags and Votes
                this.Clear_Tags_Votes(post);
                
                //Clear out answers
                if (postType == PostType.Question)
                {
                    var answers = (from a in db.Posts.OfType<Answer>()
                                   where a.QuestionID == post.ID
                                   select a).ToList();
                    foreach (var a in answers)
                    {
                        this.Clear_Tags_Votes(a);
                        db.Posts.DeleteObject(a);
                    }
                }
                db.SaveChanges();

                db.Posts.DeleteObject(post);
                db.SaveChanges();
            }
            else
            {
                throw new Exception("The current user is not the author of this post.");
            }

            if (postType == PostType.Question)
            {//Question Deleted
                return RedirectToAction("Index", "Home");
            }
            else
            {//Answer Deleted
                return RedirectToAction("Details", new { questionID = questionID });
            }
        }

        private void Clear_Tags_Votes(Post post)
        {
            //Clear out tags
            var tags = (from t in post.Tags//This line just want to load tags into memory.
                        select t).ToList();
            foreach (var t in tags)
            {
                post.Tags.Remove(t);
            }
            db.SaveChanges();
            //Clear out votes
            var votes = (from v in post.Votes
                         select v).ToList();
            foreach (var vote in votes)
            {
                db.Votes.DeleteObject(vote);
            }
            db.SaveChanges();
        }
    }
}
